﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace TrackingSim.GraphicSystem.Models
{
    public class ObjectMesh : ResourceSystem.Resource
    {
        private string m_model;
        private List<VertexPositionNormalTexture> m_vertices;
        private VertexPositionNormalTexture[] m_vertices_array;
        private List<Vector3> m_vertex_list;
        private List<Vector3> m_normal_list;
        private List<Face> m_faces;
        private Dictionary<int, int> m_absolute_vertex_index_map;
        private Dictionary<int, int> m_absolute_normal_index_map;

        private VertexBuffer m_vertex_buffer;
        private bool m_vertex_buffer_allocated;
        private int m_triangle_count;

        public ObjectMesh()
        {
            m_vertices = new List<VertexPositionNormalTexture>();
            m_vertex_list = new List<Vector3>();
            m_normal_list = new List<Vector3>();
            m_faces = new List<Face>();
            m_absolute_normal_index_map = new Dictionary<int, int>();
            m_absolute_vertex_index_map = new Dictionary<int, int>();
            m_vertex_buffer_allocated = false;
            m_triangle_count = 0;
        }

        public string ModelName
        {
            get { return m_model; }
            set { m_model = value; }
        }

        public List<VertexPositionNormalTexture> Vertices
        {
            get { return m_vertices; }
        }

        public VertexPositionNormalTexture[] VerticesArray
        {
            get { return m_vertices_array; }
        }

        public List<Face> Faces
        {
            get { return m_faces; }
        }

        public int TriangleCount
        {
            get { return m_triangle_count; }
        }

        public VertexBuffer VertBuffer
        {
            get { return m_vertex_buffer; }
        }

        public void addVertex(Vector3 position, int abs_vertex_index)
        {
            m_vertex_list.Add(position);

            if(!m_absolute_vertex_index_map.ContainsKey(abs_vertex_index))
            {
                m_absolute_vertex_index_map.Add( abs_vertex_index, m_vertex_list.Count - 1 );
            }
        }

        public void addNormal(Vector3 normal, int abs_normal_index)
        {
            m_normal_list.Add(normal);

            if (!m_absolute_normal_index_map.ContainsKey(abs_normal_index))
            {
                m_absolute_normal_index_map.Add(abs_normal_index, m_normal_list.Count - 1);
            }
        }

        public void addFace(Face f)
        {
            m_faces.Add(f);
        }

        private int getLocalVertexIndex(int abs_vertex_index)
        {
            return m_absolute_vertex_index_map[abs_vertex_index];
        }

        private int getLocalNormalIndex(int abs_normal_index)
        {
            return m_absolute_normal_index_map[abs_normal_index];
        }

        public void buildVertices(List<Vector3> vertices, List<Vector3> normals)
        {
            foreach (Face f in m_faces)
            {
                for (int i = 0; i < f.VertexIndices.Count; ++i)
                {
                    VertexPositionNormalTexture vert = new VertexPositionNormalTexture();

                    if (f.NormalIndices.Count > 0)
                    {
                        int normal_index = f.NormalIndices[i];
                        vert.Normal = normals[normal_index - 1];
                    }

                    int vertex_index = f.VertexIndices[i];
                    vert.Position = vertices[vertex_index - 1];

                    vert.TextureCoordinate = new Vector2(0.0f, 0.0f);

                    m_vertices.Add(vert);
                }
            }

            m_vertices_array = m_vertices.ToArray();

            m_vertices.Clear();
            m_vertex_list.Clear();
            m_normal_list.Clear();

            m_triangle_count = m_faces.Count;

            m_faces.Clear();

            GC.Collect();
        }

        public void allocateVertexBuffer(GraphicsDevice device)
        {
            if (!m_vertex_buffer_allocated)
            {
                m_vertex_buffer = new VertexBuffer(device, VertexPositionNormalTexture.VertexDeclaration, VerticesArray.Length, BufferUsage.WriteOnly);
                m_vertex_buffer.SetData<VertexPositionNormalTexture>(VerticesArray);
                m_vertex_buffer_allocated = true;

                m_vertices_array = null;

                GC.Collect();
            }
        }

        public void draw(GraphicsDevice device)
        {
            allocateVertexBuffer(device);

            device.SetVertexBuffer(VertBuffer);
            device.DrawPrimitives(PrimitiveType.TriangleList, 0, TriangleCount);
        }

    }
}
